package com.miplus.ccrm.core.service.collect;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.github.yulichang.base.MPJBaseServiceImpl;
import com.github.yulichang.wrapper.MPJLambdaWrapper;
import com.miplus.ccrm.common.*;
import com.miplus.ccrm.core.controller.vo.credit.*;
import com.miplus.ccrm.core.controller.vo.ficase.*;
import com.miplus.ccrm.core.mapper.*;
import com.miplus.ccrm.core.pojo.*;
import com.miplus.ccrm.core.security.JCommonData;
import com.miplus.ccrm.core.service.action.FiDelayService;
import com.miplus.ccrm.core.service.conf.FiDistbatchHistService;
import com.miplus.ccrm.core.service.conf.MsgAlertsService;
import com.miplus.ccrm.core.utils.EnumConfirmStatus;
import com.miplus.ccrm.core.utils.EnumFiCaseQueue;
import com.miplus.ccrm.core.utils.EnumFiCaseStatus;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.ListUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.math.BigDecimal;
import java.text.DecimalFormat;
import java.text.SimpleDateFormat;
import java.util.*;

/**
* @author pc
* @description 针对表【fi_creditcases(案件表)】的数据库操作Service实现
* @createDate 2024-03-26 21:59:40
*/
@Slf4j
@Service
public class FiCreditcasesServiceImpl extends MPJBaseServiceImpl<FiCreditcasesMapper, FiCreditcases>
    implements FiCreditcasesService {
    @Resource
    private FiDistbatchHistService fiDistbatchHistService;
    @Resource
    private FiCreditcasesHistService fiCreditcasesHistService;
    @Resource
    private FiDelayService fiDelayService;
    @Resource
    private MsgAlertsService msgAlertsService;
    @Resource
    private JCommonData jCommonData;

    //配置案件导航菜单最大的逾期期数，超过此期数就显示M99
    @Value("${ccrm.nav.mthsmax:}")
    public Integer nav_mthsmax;

    @Override
    public IPage<FiAppointResVo> fiAppointCaseList(String $usrid, FiAppointReqVo paras)
    {//本人今日预约案件LIST
        SimpleDateFormat formatter=new SimpleDateFormat("yyyy-MM-dd");
        //String today=formatter.format(new Date())+" 00:00:00";
        String today=formatter.format(new Date()); //2024-08-29 不能加时间，否则就变成当前时间了
        return selectJoinListPage(new Page<>(paras.getPageNum(), paras.getPageSize()),
                FiAppointResVo.class,
                new MPJLambdaWrapper<FiCreditcases>()
                        .selectAs("fun_getnames('BUSINESS.TYPE',t.prodtype)",FiAppointResVo::getProdtype)
                        .selectAs(FiCreditcases::getCreditcaseid,FiAppointResVo::getFicaseid)
                        .selectAs("fun_getnames('COLLECT.ODUE',t.mths_odue)",FiAppointResVo::getMthsOdue)
                        .selectAs(FiCreditcases::getOdudPrin,FiAppointResVo::getDebtprin)
                        .leftJoin(ConCustomer.class,"cu",ConCustomer::getCustomernumber,FiCreditcases::getCustomernumber)
                        .select(FiCreditcases::getAppointdt,FiCreditcases::getLastccdt,FiCreditcases::getAssigndt,FiCreditcases::getSendtousr,
                                FiCreditcases::getRepay,FiCreditcases::getMthsDay)
                        .select(ConCustomer::getCustname)
                        .isNotNull(FiCreditcases::getSendtousr)
                        .eq(FiCreditcases::getStatus,EnumFiCaseStatus.Assign.value)
                        .eq(FiCreditcases::getSendtousr,$usrid)
                        .ge(FiCreditcases::getAppointdt,JPubUtils.StringToDate(today))
                        .orderByAsc(FiCreditcases::getAppointdt)
        );
    }

    @Override
    public List<GrpCaseCountVo> grpCaseCountList(GrpListReqVo paras)
    {
        return selectJoinList(GrpCaseCountVo.class,
                new MPJLambdaWrapper<FiCreditcases>()
                        .selectAs("(select g.grpname from sys_grp g where g.grpid=t.sendtodept)",GrpCaseCountVo::getGroupname)
                        .selectAs("count(1)",GrpCaseCountVo::getAllcount)
                        .selectAs("sum(case when f.ccc in('Q','S') then 1 else 0 end)",GrpCaseCountVo::getQcount)
                        .selectAs("sum(case when f.ccc='R' then 1 else 0 end)",GrpCaseCountVo::getRcount)
                        .selectAs("sum(case when f.ccc='S' then 1 else 0 end)",GrpCaseCountVo::getScount)
                        .isNotNull(FiCreditcases::getSendtodept)
                        .isNotNull(FiCreditcases::getCcc)
                        .in(FiCreditcases::getCcc,"Q","R","S","F")
                        .in(JPubUtils.IsNotEmpty(paras.getGrplist()),FiCreditcases::getSendtodept,paras.getGrplist())
                        .groupBy(FiCreditcases::getSendtodept)
        );
    }

    private List<DistResVo> preDistCaseByAvg(List<DistCaseInfoVo> caseList, List<ValRadioVo> keyList,Integer pridelay,boolean isUsrDelay) {
        if(pridelay==null) pridelay=1;

        List<DistResVo> distResVoList=new LinkedList<>();

        log.info("preDistCaseByAvg:CaseCount={},pridelay={}", caseList.size(),pridelay);
        InitDistList(distResVoList,caseList,keyList,true);
        if(pridelay==1) {//是否需要考虑留案情况
            distBuildDelay(distResVoList, caseList,isUsrDelay,false);
        }

        snakeDistribute(distResVoList,caseList,false);

        return distResVoList;
    }

    private List<DistResVo> preDistCaseByNumRadio(List<DistCaseInfoVo> caseList, List<ValRadioVo> keyList,Integer pridelay,boolean isUsrDelay) {
        //按案件数量比例分配模式预分案
        if(pridelay==null) pridelay=1;
        List<DistResVo> distResVoList=new LinkedList<>();

        log.info("preDistCaseByNumRadio:CaseCount={},pridelay={}", caseList.size(),pridelay);

        InitDistList(distResVoList,caseList,keyList,false);

        if(pridelay==1) {//是否需要考虑留案情况
            distBuildDelay(distResVoList, caseList,isUsrDelay,false);
        }

        snakeDistribute(distResVoList,caseList,false);

        return distResVoList;
    }

    private List<DistResVo> preDistCaseByAmtRadio(List<DistCaseInfoVo> caseList, List<ValRadioVo> keyList,Integer pridelay,boolean isUsrDelay) {
        //按金额比例分配模式预分案
        if(pridelay==null) pridelay=1;

        List<DistResVo> distResVoList=new LinkedList<>();

        log.info("preDistCaseByAmtRadio:CaseCount={},pridelay={}", caseList.size(),pridelay);

        InitDistList(distResVoList,caseList,keyList,false);

        if(pridelay==1) {//是否需要考虑留案情况
            distBuildDelay(distResVoList, caseList,isUsrDelay,true);
        }

        snakeDistribute(distResVoList,caseList,true);

        return distResVoList;
    }

    private int InitDistList(List<DistResVo> distResVoList,List<DistCaseInfoVo> amtList,List<ValRadioVo> keyList,boolean isavgmode)
    {
        int casecount=amtList.size();
        double radio=0.0,lastradio=0.0;
        int precount=0;
        BigDecimal preamount=new BigDecimal(0.00);
        BigDecimal totalAmt=GetTotalAmount(amtList);

        if(isavgmode) {
            radio = Math.round((100.0 / keyList.size())*100)/100.0; //计算平均比例,保留2位小数
            precount = (int)Math.round(casecount*radio/100.0); //计算平均数量
            preamount=totalAmt.multiply(new BigDecimal(radio/100.0)).setScale(2,2);//保留2位小数,2024-09
        }

        log.info("InitDistList:CaseCount={},DistGroupCount={},totalAmt={},isavgmode={},precount={},preamount={}",
                amtList.size(),keyList.size(),totalAmt,isavgmode,precount,preamount
        );
        int poslist=0,lastcount=0;

        BigDecimal lastamount=new BigDecimal(0.0);

        for(ValRadioVo g : keyList){
            DistResVo distResVo=new DistResVo();
            distResVo.setKey(g.getKey());
            distResVo.setCount(0);
            if(isavgmode){
                distResVo.setRadio(radio);
                distResVo.setPreradio(radio);
            }else {
                distResVo.setRadio(g.getRadio());
                distResVo.setPreradio(g.getRadio());
                precount =(int)Math.round(g.getRadio() *casecount / 100);
                preamount=totalAmt.multiply(new BigDecimal(g.getRadio()/100.0)).setScale(2,2);//保留2位小数
            }

            distResVo.setPrecount(precount);
            distResVo.setPreamount(preamount);
            distResVo.setDelaycount(0);
            distResVo.setAmount(new BigDecimal("0.00"));
            distResVo.setDelayamt(new BigDecimal("0.00"));
            distResVo.setFicaseList(new LinkedList<>());
            if(poslist==keyList.size()-1){//设置最后一组
                distResVo.setRadio(100-lastradio);
                distResVo.setPreradio(100-lastradio);
                distResVo.setPrecount(casecount-lastcount);
                distResVo.setPreamount(totalAmt.subtract(lastamount));
            }
            distResVoList.add(distResVo);
            log.info("InitDistList:{}",distResVo);
            poslist++;
            lastcount+=precount;
            lastradio+=distResVo.getRadio();
            lastamount=lastamount.add(preamount);
        }

        return distResVoList.size();
    }

    private int snakeDistribute(List<DistResVo> distResVoList,List<DistCaseInfoVo> amtList,boolean isamtradio)
    {//改进型蛇型分配算法
        int snakemodepos=0;
        int distgrpcount=distResVoList.size();

        int maxpos=0;
        int startpos=0,startpos2=amtList.size()-1;
        log.info("snakeDistribute:CaseCount={},distgrpcount={},startpos2={}",amtList.size(),distgrpcount,startpos2);
        BigDecimal disttotalamt=new BigDecimal(0.0);
        double dAmt=0.0;
        int kkcnt=0;
        for(int k=0;k<amtList.size();k+=0){
            if(snakemodepos % 2 ==0) {
                for (int i = 0; i < distResVoList.size(); i++) {
                    if(kkcnt>=amtList.size()){
                        //2024-09-20 分配的案件比组数量少，比如2个案件分给3个组
                        break;
                    }
                    DistCaseInfoVo pCase=amtList.get(startpos);
                    DistResVo distResVo = distResVoList.get(i);
                    if(isamtradio){//按金额比例分配
                        if(distResVo.getAmount().compareTo(distResVo.getPreamount())<0){
                            dAmt=SetDistResValue(distResVo, pCase,false);
                            disttotalamt=disttotalamt.add(new BigDecimal(dAmt));
                            startpos++;
                            k++;
                            kkcnt++;
                        }
                    }
                    else if(distResVo.getCount()<distResVo.getPrecount()) {//如果大于等于数量，就不能再取了
                        dAmt=SetDistResValue(distResVo, pCase,false);
                        disttotalamt=disttotalamt.add(new BigDecimal(dAmt));
                        startpos++;
                        k++;
                        kkcnt++;
                    }
                }
            }
            else{
                for (int i = 0; i < distResVoList.size(); i++) {
                    if(kkcnt>=amtList.size()){
                        //2024-09-20 分配的案件比组数量少，比如2个案件分给3个组
                        break;
                    }
                    DistCaseInfoVo pCase=amtList.get(startpos2);
                    DistResVo distResVo = distResVoList.get(i);
                    if(isamtradio){//按金额比例分配
                        if(distResVo.getAmount().compareTo(distResVo.getPreamount())<0){
                            dAmt=SetDistResValue(distResVo, pCase,false);
                            disttotalamt=disttotalamt.add(new BigDecimal(dAmt));
                            startpos2--;
                            k++;
                            kkcnt++;
                        }
                    }
                    else if(distResVo.getCount()<distResVo.getPrecount()) {
                        dAmt=SetDistResValue(distResVo, pCase,false);
                        disttotalamt=disttotalamt.add(new BigDecimal(dAmt));
                        startpos2--;
                        k++;
                        kkcnt++;
                    }
                }
            }
            snakemodepos++;
            maxpos++;
            if(maxpos>=JPubUtils.distMaxCount){//避免死循环
                log.warn("snakeDistribute.fail:maxpos={},k={},startpos={},startpos2={},maxpos={},totalamt={}",
                        maxpos,k,startpos,startpos2,maxpos,disttotalamt
                );
                throw new ServiceException(EnumRCode.DistCaseFail.value, EnumRCode.DistCaseFail.name);
            }
        }

        int disttotal=amtList.size();
        log.info("snakeDistribute:startpos={}/{},maxpos={},snakemodepos={},kkcnt={},disttotal={},disttotalamt={}",
                startpos,startpos2,maxpos,snakemodepos,kkcnt,disttotal,disttotalamt);
        //计算实际的分配比例（案件数比例和案件金额比例）
        for (DistResVo distResVo : distResVoList) {
            double amt=distResVo.getAmount().doubleValue();
            //int r=(int)Math.round((amt*100.0)/disttotalamt.doubleValue());
            double r=0.0;
            if(disttotalamt.doubleValue()==0){//224-11-01
                distResVo.setRadioamt("0.00");
            }else {
                r = (amt * 100.0) / disttotalamt.doubleValue();
                distResVo.setRadioamt(String.format("%.2f", r));
            }
            //r=Math.round(distResVo.getCount()*100/disttotal);
            r=distResVo.getCount()*100.0/disttotal;
            distResVo.setRadiocount(String.format("%.2f",r));
            if(log.isDebugEnabled()) {
                log.debug("snakeDistribute:{}", distResVo);
            }
            else{
                log.info("snakeDistribute({}):count={},amount={},delaycount={},delayamt={},radio={},radiocount={},radioamt={},precount={},preamount={},ficasecount={}",
                        distResVo.getKey(),distResVo.getCount(),distResVo.getAmount(),distResVo.getDelaycount(),distResVo.getDelayamt(),distResVo.getRadio(),
                        distResVo.getRadiocount(),distResVo.getRadioamt(),distResVo.getPrecount(),distResVo.getPreamount(),distResVo.getFicaseList().size()
                );
            }
        }

        amtList.clear();

        return disttotal;
    }

    private BigDecimal GetTotalAmount(List<DistCaseInfoVo> amtList)
    {
        BigDecimal amt=new BigDecimal(0.0);
        for(DistCaseInfoVo pCase : amtList) {
            if(pCase.getOdudPrin()!=null) {
                amt=amt.add(pCase.getOdudPrin());
            }
        }
        return amt;
    }

    private double SetDistResValue(DistResVo distResVo,DistCaseInfoVo pCase,boolean isdelay)
    {
        DecimalFormat df=new DecimalFormat("#.00");
        double amt=JPubUtils.ObjectToDouble(pCase.getOdudPrin());
        if(isdelay){
            distResVo.setDelaycount(distResVo.getDelaycount()+1);
            double d = distResVo.getDelayamt().doubleValue() + amt;
            distResVo.setDelayamt(new BigDecimal(df.format(d)));
        }else{
            distResVo.setCount(distResVo.getCount() + 1);
            double d = distResVo.getAmount().doubleValue() + amt;
            distResVo.setAmount(new BigDecimal(df.format(d)));
        }
        log.info("SetDistResValue:{}",pCase);
        distResVo.getFicaseList().add(Integer.parseInt(pCase.getCreditcaseid().toString()));
        return amt;
    }

    private int distBuildDelay(List<DistResVo> distResVoList,List<DistCaseInfoVo> amtList,boolean isUsrDelay,boolean isAmountMode)
    {//isAmountMode=true 按金额比率,2024-09-03
        int delaycount=0;
        for(int i=amtList.size()-1;i>=0;i--){
            DistCaseInfoVo pCase=amtList.get(i);
            String delaykey=pCase.getSendtodept();
            if(JPubUtils.IsNotEmpty(delaykey)){
                if(isUsrDelay){
                    delaykey=pCase.getSendtousr();
                    if(JPubUtils.IsEmpty(delaykey)){
                        log.info("distBuildDelay({}):nofind sendtousr",pCase.getCreditcaseid());
                        continue;
                    }
                }
                for (DistResVo distResVo : distResVoList) {
                    if(JPubUtils.IsEqual(delaykey,distResVo.getKey())){
                        delaycount++;
                        SetDistResValue(distResVo,pCase,true);
                        amtList.remove(pCase);
                    }
                }
            }
        }

        //没有留案数，不需要计算
        if(delaycount<=0) {
            log.info("distBuildDelay:nodelay");
            return delaycount;
        }

        int sjcount=amtList.size(); //去掉留案户数剩余预分案总户数
        double lastradio=0.0;
        BigDecimal lastamt=new BigDecimal(0.0);
        int poslist=0,lastcount=0;

        BigDecimal totalAmount=new BigDecimal(0.0);
        for(int i=0;i<amtList.size();i++){//去掉留案户数剩余预分案总金额
            DistCaseInfoVo pCase=amtList.get(i);
            totalAmount=totalAmount.add(pCase.getOdudPrin());
        }

        log.info("distBuildDelay:count={},amount={},delaycount={},isAmountMode={}",sjcount,totalAmount,delaycount,isAmountMode);

        for (DistResVo distResVo : distResVoList) {
            BigDecimal preAmount = distResVo.getPreamount().subtract(distResVo.getDelayamt());
            int precount = distResVo.getPrecount() - distResVo.getDelaycount();
            double radio = 0.0;
            if(isAmountMode) {
                //这样如果数据量太大，可能会引起divide Non-terminating decimal expansion 异常，需要增加BigDecimal.ROUND_CEILING，2024-10-15
                //radio = (preAmount.multiply(new BigDecimal(100.0)).divide(totalAmount)).doubleValue();
                radio = (preAmount.doubleValue()*100.0) / totalAmount.doubleValue();
            }
            else{
                radio = (precount * 100.0) / sjcount;
            }
            radio=Math.round(radio*100)/100.0; //取2位小数
            distResVo.setPreradio(radio);
            distResVo.setPreamount(preAmount);
            distResVo.setPrecount(precount);
            if (poslist == distResVoList.size() - 1) {//设置最后一组
                distResVo.setPreradio(100.0 - lastradio);
                distResVo.setPrecount(sjcount - lastcount);
                distResVo.setPreamount(totalAmount.subtract(lastamt));
            }
            log.info("distBuildDelay:{}.SetRadio={}/{},Count={}/{},Amt={}/{},delaycount={},delayamt={},ficaseList={}",
                    distResVo.getKey(), distResVo.getRadio(), distResVo.getPreradio(), distResVo.getCount(), distResVo.getPrecount(),
                    distResVo.getAmount(), distResVo.getPreamount(), distResVo.getDelaycount(),
                    distResVo.getDelayamt(),distResVo.getFicaseList()
            );
            poslist++;
            lastradio += radio;
            lastcount += precount;
            lastamt=lastamt.add(preAmount);
        }

        return delaycount;
    }


    private int distToGroup(String distgrp,String $usrid,String batchid,Integer actiontype,Date bindenddt,DistCaseInfoVo p,Date distdt)
    {//委案时,退案时需要记录逾期期数，已出账单欠款，本金，利息，费用，委案期间记录：还款金额
        FiCreditcases fi = new FiCreditcases();
        Integer ficaseid=p.getCreditcaseid();
        fi.setCreditcaseid(ficaseid);
        fi.setStatus(EnumFiCaseStatus.Distribute.value);
        fi.setStatusdt(new Date());
        fi.setCcc(EnumFiCaseQueue.Q.name);
        fi.setDistbatchid(batchid);
        fi.setBindenddt(bindenddt);
        fi.setDistdt(distdt);
        fi.setDistusr($usrid);
        fi.setDistributetype(actiontype);
        fi.setSendtodept(distgrp);
        if(JPubUtils.IsEmpty(p.getIndt())){
            fi.setIndt(new Date());
        }
        //V2.0模式，在案件生成是就把金额，逾期期数，天数就写到案件表了，每天更新案件表信息，不需要单独取了
        //BuildFiCaseAmt(fi,p);

        int ret=baseMapper.update(fi,new UpdateWrapper<FiCreditcases>()
                .lambda()
                .eq(FiCreditcases::getCreditcaseid,ficaseid)
                .set(FiCreditcases::getRepay,null)
                .set(FiCreditcases::getRepaydt,null)
                .set(FiCreditcases::getAssigndt,null)
                .set(FiCreditcases::getAssigntype,null)
                .set(FiCreditcases::getAssignusr,null)
                .set(FiCreditcases::getSendtousr,null)
        );

        return ret;
    }

    private int assginToUser(String assusr,String $usrid,String batchid,
                             Integer actiontype,DistCaseInfoVo p,String assgrp,Date bindenddt,Date distdt
    )
    {
        FiCreditcases fi = new FiCreditcases();
        Integer ficaseid=p.getCreditcaseid();
        fi.setCreditcaseid(ficaseid);
        fi.setStatus(EnumFiCaseStatus.Assign.value);
        fi.setStatusdt(new Date());
        fi.setCcc(EnumFiCaseQueue.Q.name);
        if(JPubUtils.IsNotEmpty(assgrp)){
            //这种情况下直接派工（分配）给组下催员，主要针对内催组,assbatchid=distbatchid
            distToGroup(assgrp,$usrid,batchid,actiontype,bindenddt,p,distdt);
        }
        fi.setAssignbatchid(batchid);
        fi.setAssigndt(new Date());
        fi.setAssignusr($usrid);
        fi.setAssigntype(actiontype);
        fi.setSendtousr(assusr);
        int ret=baseMapper.updateById(fi);

        log.info("assginToUser:ficaseid={} To {} Ret={}",ficaseid,assusr,ret);

        return ret;
    }

    private List<DistCaseInfoVo> getDistCaseList(List<Integer> caseList)
    {//委案时时记录：逾期期数，已出账单欠款，己出账单欠款(当前余额)本金，己出账单欠款(当前余额)利息，己出账单欠款(当前余额)费用
        MPJLambdaWrapper<FiCreditcases> quyWrapper=new MPJLambdaWrapper<FiCreditcases>()
                .select(FiCreditcases::getCreditcaseid, FiCreditcases::getStatus, FiCreditcases::getCcc,
                        FiCreditcases::getOdudPrin,FiCreditcases::getIndt)
                .eq(FiCreditcases::getCcc, EnumFiCaseQueue.O.name);

        if(caseList.size()<=1000) {
            quyWrapper.in(FiCreditcases::getCreditcaseid, caseList);
        }
        else{
            List<List<Integer>> newList= ListUtils.partition(caseList,1000);
            quyWrapper.and(p->{
                for(List<Integer> list : newList){
                    p.or().in(FiCreditcases::getCreditcaseid,list);
                }
            });
        }

        return selectJoinList(DistCaseInfoVo.class,quyWrapper);
    }

    private List<DistCaseInfoVo> getAssCaseList(List<Integer> caseList)
    {
        MPJLambdaWrapper<FiCreditcases> quyWrapper=new MPJLambdaWrapper<FiCreditcases>()
                .select(FiCreditcases::getCreditcaseid, FiCreditcases::getStatus, FiCreditcases::getCcc,
                        FiCreditcases::getOdudPrin,FiCreditcases::getIndt)
                .eq(FiCreditcases::getCcc, EnumFiCaseQueue.Q.name);

        if(caseList.size()<=1000) {
            quyWrapper.in(FiCreditcases::getCreditcaseid, caseList);
        }
        else{
            List<List<Integer>> newList= ListUtils.partition(caseList,1000);
            quyWrapper.and(p->{
                for(List<Integer> list : newList){
                    p.or().in(FiCreditcases::getCreditcaseid,list);
                }
            });
        }

        return selectJoinList(DistCaseInfoVo.class,quyWrapper);
    }

    //V2.0不在使用，案件生成时就写入了，每天跑批时更新
    /*
    @Override
    public LinkedHashMap getFiCaseAcctBal(Integer ficaseid)
    {
        return selectJoinOne(LinkedHashMap.class,
                new MPJLambdaWrapper<FiCreditcases>()
                        .selectAs("(fc_getpayamt(t.credit_acct, DATE_FORMAT(t.distdt,'%Y-%m-%d'), DATE_FORMAT(CURRENT_DATE,'%Y-%m-%d')))",FiCreditcases::getRepay)
                        .leftJoin(CreditAccountBalance.class,"cdb",CreditAccountBalance::getAcct, FiCreditcases::getCreditAcct)
                        .leftJoin(CreditAccount.class,"cda",CreditAccount::getAcct,FiCreditcases::getCreditAcct)
                        .select(CreditAccountBalance::getSettledBillAmt, CreditAccountBalance::getSettledBillPrin, CreditAccountBalance::getSettledBillInt,
                                CreditAccountBalance::getSettledBillFee)
                        .select(CreditAccount::getMthsOdue,CreditAccount::getMthsDay)
                        .eq(FiCreditcases::getCreditcaseid, ficaseid)
        );
    }*/

    public boolean doDistCaseTo(List<DistResVo> distResVoList,String batchid,String $usrid,Integer binddays,
                                         Integer actiontype,Integer distmode,String batchname,Double aimrate) {
        if(JPubUtils.IsEmpty(binddays)||binddays<=0){
            binddays=90;
        }
        Date bindenddt = JPubUtils.addDays(new Date(), binddays);
        log.info("doDistCaseTo:distmode={},batchid={},binddays={}/{}", distmode,batchid,binddays,bindenddt);

        BigDecimal distamt=new BigDecimal(0.0);
        Date distdt=new Date();
        int distokcnt=0,distcount=0;
        String distdtime=JPubUtils.DTimeToString(new Date());
        for (DistResVo distResVo : distResVoList) {//分案组
            List<Integer> caseList=distResVo.getFicaseList();
            if(JPubUtils.IsEmpty(caseList)){//案件数少于选择的组数量时
                continue;
            }
            distcount+=caseList.size();
            List<DistCaseInfoVo> querylist = getDistCaseList(caseList);
            distokcnt+=querylist.size();
            for (int i=0;i<querylist.size();i++) {
                DistCaseInfoVo p=querylist.get(i);
                BigDecimal amt=p.getOdudPrin();
                if(amt!=null){
                    distamt=distamt.add(amt);
                }
                distToGroup(distResVo.getKey(), $usrid, batchid, actiontype, bindenddt, p,distdt);
            }
            log.info("doDistCaseTo({}):count={},amount={},delaycount={},delayamt={},radio={},radiocount={},radioamt={},precount={},preamount={},ficasecount={}",
                    distResVo.getKey(),distResVo.getCount(),distResVo.getAmount(),distResVo.getDelaycount(),distResVo.getDelayamt(),distResVo.getRadio(),
                    distResVo.getRadiocount(),distResVo.getRadioamt(),distResVo.getPrecount(),distResVo.getPreamount(),distResVo.getFicaseList().size()
            );
            //分案成功后把留案取消
            fiDelayService.doCancel($usrid,caseList,EnumConfirmStatus.Pass.value);
            querylist.clear();
        }

        SaveDistHist($usrid,batchid,batchname,binddays,distmode,distokcnt,
                distcount,distdtime,distamt,distdt,actiontype,aimrate
        );

        return true;
    }

    private void SaveDistHist(String usrid,String batchid,String batchname,Integer binddays,Integer distmode,Integer okcnt,
                              Integer distcnt,String distdtime,BigDecimal distamt,Date distdt,Integer acttype,Double aimrate)
    {
        log.info("SaveDistHist:{},OkCnt={},distcnt={}",batchid,okcnt,distcnt);
        FiDistbatchHist hist=new FiDistbatchHist();
        hist.setBatchid(batchid);
        hist.setBatchname(batchname);
        hist.setActiondt(distdt);
        hist.setActionusr(usrid);
        hist.setActiontype(acttype.toString()); //自动/手动
        hist.setBinddays(binddays);
        hist.setDistmode(distmode);
        hist.setOkcnt(okcnt);
        hist.setDistcnt(distcnt);
        hist.setDistamt(distamt);
        if(JPubUtils.IsNotEmpty(aimrate)){
            hist.setAimrate(aimrate);
        }
        fiDistbatchHistService.getBaseMapper().insert(hist);
        log.info("SaveDistHist:{} updateToHist",batchid);

        int ret=baseMapper.updateToHist(batchid,distdtime);
        log.info("SaveDistHist:{} Done,Ret={}",batchid,ret);

        //检查处理批次多次增加数据的情况，如果不等以后一次为准
        Double firstaim=fiDistbatchHistService.getFristAimRate(batchid);
        if(!JPubUtils.IsEqual(firstaim,aimrate)){
            if(JPubUtils.IsNotEmpty(aimrate)){
                firstaim=aimrate;
            }
            fiDistbatchHistService.getBaseMapper().update(null, new UpdateWrapper<FiDistbatchHist>()
                    .lambda()
                    .eq(FiDistbatchHist::getBatchid,batchid)
                    .set(FiDistbatchHist::getAimrate, firstaim)
            );
        }
    }

    private List<DistCaseInfoVo> caseSortByAmt(List<Integer> caseList)
    {//案件按逾期金额排序
        MPJLambdaWrapper<FiCreditcases> quyWrapper=new MPJLambdaWrapper<FiCreditcases>()
                .selectAs("fc_getDelayGroup(t.creditcaseid)",FiCreditcases::getSendtodept)
                .select(FiCreditcases::getCreditcaseid,FiCreditcases::getOdudPrin,FiCreditcases::getSendtousr);

        if(caseList.size()<=1000) {
            quyWrapper.in(FiCreditcases::getCreditcaseid, caseList);
        }
        else{
            List<List<Integer>> newList= ListUtils.partition(caseList,1000);
            quyWrapper.and(p->{
                for(List<Integer> list : newList){
                    p.or().in(FiCreditcases::getCreditcaseid,list);
                }
            });
        }
        quyWrapper.orderByDesc(FiCreditcases::getOdudPrin);

        return selectJoinList(DistCaseInfoVo.class,quyWrapper);
    }

    @Override
    public List<DistResVo> fiCaseDistribute(JSysUser user, DistReqVo paras, List<Integer> caseList)
    {
        Integer distmode=paras.getDismode();
        if((distmode==JPubUtils.DIST_MODE_CASERADIO || distmode==JPubUtils.DIST_MODE_AMTRADIO)
            &&JPubUtils.IsEmpty(paras.getGrpList())){
            throw new ServiceException(EnumRCode.ArgErr.value, EnumRCode.ArgErr.name);
        }

        String batchid=paras.getBatchid();
        if(!CheckDistBindday(batchid,paras.getBinddays())){
            throw new ServiceException(EnumRCode.ArgErr.value, "选择历史批次时绑定天数不正确!");
        }

        String $usrid=user.getUsrid();
        String rkey = jCommonData.getDistResultKey($usrid);
        List<DistResVo> distResVoList = null;
        List<DistResVo> distResultList = null;
        if (paras.getIspredist() == JPubUtils.PRE_OPERATE) { //预分案
            distResVoList=preDistribute(paras,caseList);

            //2025-06-09 结果集可能会很大(比如使用查询条件模式取数），需要保存在redis里面，不能返回客户端
            boolean b = jCommonData.setData(rkey, distResVoList, JPubUtils.distExpiration);
            log.info("fiCaseDistribute.setPreDistResult({},{}):ret={}", rkey, JPubUtils.distExpiration, b);

            distResultList=buildDistResult(distResVoList);

            caseList.clear();
        }
        else{
            String batchname=paras.getBatchname();
            if(JPubUtils.IsEmpty(batchid)){
                SimpleDateFormat sdf=new SimpleDateFormat("yyMMddHHmmss");
                batchid="M"+sdf.format(new Date());
            }
            if(JPubUtils.IsEmpty(batchname)){
                batchname=batchid;
            }
            Object o=jCommonData.getData(rkey);
            if(o==null){
                log.warn("fiCaseDistribute:nofinddata {}",rkey);
                throw new ServiceException(EnumRCode.NoFindPreDist.value, EnumRCode.NoFindPreDist.name);
            }else{
                distResVoList=(List<DistResVo>)o;
                jCommonData.delData(rkey);
            }
            doDistCaseTo(distResVoList,batchid, $usrid, paras.getBinddays(),
                    paras.getActiontype(), distmode, batchname,paras.getAimrate()
            );
            distResultList=buildDistResult(distResVoList);
            distResVoList.clear();
        }

        return distResultList;
    }

    //预分案操作
    @Override
    public List<DistResVo> preDistribute(DistReqVo paras, List<Integer> caseList)
    {
        Integer distmode=paras.getDismode();
        List<DistResVo> distResVoList;
        List<DistCaseInfoVo> distCaseList= caseSortByAmt(caseList);
        if (distmode == JPubUtils.DIST_MODE_CASERADIO) {
            distResVoList = preDistCaseByNumRadio(distCaseList, paras.getGrpList(), paras.getPridelay(),false);
        } else if (distmode == JPubUtils.DIST_MODE_AMTRADIO) {
            distResVoList = preDistCaseByAmtRadio(distCaseList, paras.getGrpList(), paras.getPridelay(),false);
        } else {
            distResVoList = preDistCaseByAvg(distCaseList, paras.getGrpList(), paras.getPridelay(),false);
        }
        distCaseList.clear();
        return distResVoList;
    }

    private List<DistResVo> buildDistResult(List<DistResVo> distResVoList)
    {//2025-06-09 结果集可能会很大(比如使用查询条件模式取数），不能把ficaseList返回到客户端
        List<DistResVo> resVos=new LinkedList<>();
        for(DistResVo distResVo : distResVoList){
            DistResVo rp=new DistResVo();
            BeanUtils.copyProperties(distResVo,rp);
            rp.getFicaseList().clear();
            rp.setFicaseList(null);
            resVos.add(rp);
        }
        return resVos;
    }

    public boolean doAssignCaseTo(List<DistResVo> distResVoList,String batchid,String $usrid,
                                 Integer actiontype,Integer distmode,String assgrp,Integer binddays) {
        if(JPubUtils.IsEmpty(binddays)||binddays<=0){
            binddays=90;
        }
        Date bindenddt = JPubUtils.addDays(new Date(), binddays);
        //assgrp不为空，表示直接派工
        log.info("doAssignCaseTo:distmode={},batchid={},binddays={}/{},grpid={}",
                distmode,batchid,binddays,bindenddt,assgrp
        );

        BigDecimal distamt=BigDecimal.ZERO;
        Date distdt=new Date();
        int distokcnt=0,distcount=0;
        String distdtime=JPubUtils.DTimeToString(new Date());
        for (DistResVo distResVo : distResVoList) {//分案账户（用户）LIST
            List<Integer> caseList=distResVo.getFicaseList();
            if(JPubUtils.IsEmpty(caseList)){//案件数少于选择的催员数量时
                continue;
            }
            distcount+=caseList.size();
            List<DistCaseInfoVo> querylist=null;
            if(JPubUtils.IsEmpty(assgrp)){
                querylist = getAssCaseList(caseList);
            }else {//直接派工
                querylist = getDistCaseList(caseList);
            }
            distokcnt+=querylist.size();
            for (int i=0;i<querylist.size();i++) {
                DistCaseInfoVo pCase=querylist.get(i);
                BigDecimal amt=pCase.getOdudPrin();
                if(amt!=null) {//2024-06-26
                    distamt=distamt.add(amt);
                }
                assginToUser(distResVo.getKey(), $usrid, batchid, actiontype, pCase,assgrp,bindenddt,distdt);
            }
            log.info("doAssignCaseTo({}):distcount={},okcnt={},distamt={}/count={},amount={},Delaycount={},Delayamt={},radio={},Radiocount={},Radioamt={}",
                    distResVo.getKey(),caseList.size(),querylist.size(),distamt,
                    distResVo.getCount(),distResVo.getAmount(),distResVo.getDelaycount(),distResVo.getDelayamt(),
                    distResVo.getRadio(),distResVo.getRadiocount(),distResVo.getRadioamt()

            );
            msgAlertsService.msgAlertAdd("派工提醒","有新派工案件",distResVo.getKey(),$usrid);

            if(JPubUtils.IsNotEmpty(assgrp)) {
                //直接派工成功后把留案取消
                fiDelayService.doCancel($usrid, caseList, EnumConfirmStatus.Pass.value);
            }

            querylist.clear();
        }

        if(JPubUtils.IsNotEmpty(assgrp)){
            SaveDistHist($usrid,batchid,"直接派工"+batchid,binddays,
                    distmode,distokcnt,distcount,distdtime,distamt,distdt,actiontype,null
            );
            //直接派工案件状态已经是分配状态了，需要把分案历史表案件状态改成由派工改成分配
            baseMapper.updateHistDistStatus(batchid);
        }
        int ret=baseMapper.updateToHistByAss(batchid);

        log.info("doAssignCaseTo Done,updateToHist:batchid={},ret={}",batchid,ret);

        return true;
    }

    @Override
    public List<DistResVo> fiCaseAssign(JSysUser user,AssReqVo paras,List<Integer> caseList)
    {//assreqvo.grpid 不为空表示直接派工
        Integer distmode=paras.getAssmode();
        if((distmode==JPubUtils.DIST_MODE_CASERADIO || distmode==JPubUtils.DIST_MODE_AMTRADIO)
                &&JPubUtils.IsEmpty(paras.getUsrList())){
            throw new ServiceException(EnumRCode.ArgErr.value, EnumRCode.ArgErr.name);
        }
        String $usrid=user.getUsrid();
        String rkey = jCommonData.getAssResultKey($usrid);
        List<DistResVo> distResVoList = null;
        List<DistResVo> distResultList = null;
        if(paras.getIspreass()==JPubUtils.PRE_OPERATE) {
            distResVoList=preAssign(paras,caseList);

            //2025-06-09 结果集可能会很大(比如使用查询条件模式取数），需要保存在redis里面，不能返回客户端
            boolean b = jCommonData.setData(rkey, distResVoList, JPubUtils.distExpiration);
            log.info("fiCaseAssign.setPreAssResult({},{}):ret={}", rkey, JPubUtils.distExpiration, b);

            distResultList=buildDistResult(distResVoList);
            caseList.clear();
        }
        else{
            String batchid=paras.getBatchid();
            if(JPubUtils.IsEmpty(batchid)){
                SimpleDateFormat sdf=new SimpleDateFormat("yyMMddHHmmss");
                if(JPubUtils.IsNotEmpty(paras.getGrpid())){
                    //用于直接派工批次ID
                    batchid="P"+sdf.format(new Date());
                }else {//只用于分配批次ID
                    batchid = $usrid + "." + sdf.format(new Date());
                }
            }
            Object o=jCommonData.getData(rkey);
            if(o==null){
                log.warn("fiCaseAssign:nofinddata {}",rkey);
                throw new ServiceException(EnumRCode.NoFindPreDist.value, EnumRCode.NoFindPreDist.name);
            }else{
                distResVoList=(List<DistResVo>)o;
                jCommonData.delData(rkey);
            }
            doAssignCaseTo(distResVoList,batchid, $usrid,paras.getActiontype(),
                    distmode,paras.getGrpid(),paras.getBinddays()
            );

            distResVoList.clear();
        }

        return distResultList;
    }

    //预派工操作
    @Override
    public List<DistResVo> preAssign(AssReqVo paras, List<Integer> caseList)
    {
        Integer distmode=paras.getAssmode();
        List<DistResVo> distResVoList;
        List<DistCaseInfoVo> distCaseList= caseSortByAmt(caseList);
        if(distmode==JPubUtils.DIST_MODE_CASERADIO){
            distResVoList = preDistCaseByNumRadio(distCaseList, paras.getUsrList(),1,true);
        }
        else if(distmode==JPubUtils.DIST_MODE_AMTRADIO){
            distResVoList = preDistCaseByAmtRadio(distCaseList, paras.getUsrList(),1,true);
        }
        else {
            distResVoList = preDistCaseByAvg(distCaseList, paras.getUsrList(),1,true);
        }
        distCaseList.clear();
        return distResVoList;
    }

    @Override
    public CmdResVo fiCaseRecycle(JSysUser user, RecyReqVo paras, List<Integer> recylistID, boolean oneRecycle)
    {
        //增加权限控制RECY_MODE_TOP
        String $usrid=user.getUsrid();

        if(JPubUtils.IsEmpty(recylistID)) {
            if(oneRecycle){
                log.info("fiCaseRecycle.RemoveRecycleProcess..");
                jCommonData.RemoveRecycleProcess();
            }
            return new CmdResVo(0);
        }

        Integer processCount=1;
        Date startrundt=new Date();
        SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");
        String recybatchid=$usrid+"_"+sdf.format(startrundt);
        if(paras.getRecymode()==JPubUtils.RECY_MODE_GROUP) {//回收到本组,队列不改，只改状态
            //.. 回收到本组，派工信息不在设置为空，为了保证回收时有派工催员,还款明细报表使用，2024-10-18
            //.set(FiCreditcases::getSendtousr,null)
            //.set(FiCreditcases::getSendtousr,null)
            //.set(FiCreditcases::getAssignbatchid,null)
            //.set(FiCreditcases::getAssignid,null)
            //.set(FiCreditcases::getAssigntype,null)
            //.set(FiCreditcases::getAssignusr,null)
            //.set(FiCreditcases::getAssigndt,null)
            //这里不使用in 批量更新的方式，是因为in有1000条限制，2024-12
            for(Integer recyficaseid : recylistID) {
                baseMapper.update(null, new UpdateWrapper<FiCreditcases>()
                        .lambda()
                        .eq(FiCreditcases::getCreditcaseid,recyficaseid)
                        .eq(FiCreditcases::getStatus, EnumFiCaseStatus.Assign.value)
                        .set(FiCreditcases::getAppointdt, null)
                        .set(FiCreditcases::getStatusdt, new Date())
                        .set(FiCreditcases::getRecycledsc, paras.getRemark())
                        .set(FiCreditcases::getRecycledt, new Date())
                        .set(FiCreditcases::getRecycleusr, $usrid)
                        .set(FiCreditcases::getStatus, EnumFiCaseStatus.Distribute.value)
                );
            }
            //2024-09-25回收到本组，不单独记录历史，只在案件回收几个字段上标注信息
            //baseMapper.updateToHistByID(fi.getCreditcaseid());
        }
        else if(paras.getRecymode()==JPubUtils.RECY_MODE_TOP) {
            for (Integer ficaseid : recylistID) {
                if (oneRecycle) {
                    jCommonData.SetRecycleProcess(processCount);
                }
                Date enddt=null;
                if (JPubUtils.IsNotEmpty(paras.getStopautodistday())) {
                    enddt = JPubUtils.addDays(new Date(), paras.getStopautodistday());
                }
                BigDecimal payamt = getPayAmount(ficaseid);

                //停催队列可以回收，但是回收后还是停催状态，回收只清空绑定组和催员等信息，并不改变队列属性,停催属性只有通过停催管理或标签可以改变
                baseMapper.update(null, new UpdateWrapper<FiCreditcases>()
                        .lambda()
                        .eq(FiCreditcases::getCreditcaseid, ficaseid)
                        .in(FiCreditcases::getStatus,1,2,8)
                        .set(FiCreditcases::getStatusdt, new Date())
                        .set(FiCreditcases::getRecycledsc, recybatchid)
                        .set(FiCreditcases::getRecycledt, new Date())
                        .set(FiCreditcases::getRecycleusr, $usrid)
                        .set(FiCreditcases::getStatus,EnumFiCaseStatus.Recycle.value)
                        .setSql("ccc=(case when ccc='Q' then 'O' else ccc end)")
                        .set(payamt.doubleValue()>0.0,FiCreditcases::getRepay,payamt)
                        .set(JPubUtils.IsNotEmpty(enddt),FiCreditcases::getStopautodistenddt,enddt)
                );

                processCount++;
            }
        }
        //只有回收到案件池才写历史
        if(paras.getRecymode()==JPubUtils.RECY_MODE_TOP){
            log.info("updateRecycle:startrundt={},usrid={},recybatchid={}",
                    JPubUtils.DTimeToString(startrundt),$usrid,recybatchid);
            baseMapper.insertRecycleHist(JPubUtils.DTimeToString(startrundt),$usrid,recybatchid);
            baseMapper.updateRecycleInit(JPubUtils.DTimeToString(startrundt),$usrid,recybatchid);

            fiDelayService.doCancel($usrid,recylistID,EnumConfirmStatus.Pending.value);
        }
        jCommonData.RemoveRecycleProcess();

        int recycount=recylistID.size();
        recylistID.clear();

        return new CmdResVo(recycount);
    }

    @Override
    public CmdResVo getCustFiCaseCount(String custnumber)
    {
        Long num=baseMapper.selectCount(new MPJLambdaWrapper<FiCreditcases>()
                .eq(FiCreditcases::getCustomernumber,custnumber)
                .in(FiCreditcases::getCcc,"O","Q","S")
        );

        return new CmdResVo(num.intValue(),custnumber);
    }

    @Override
    public CmdResVo getFiCaseReliefCount(Integer ficaseid)
    {
        Long num=baseMapper.selectCount(new MPJLambdaWrapper<FiCreditcases>()
                        .leftJoin(FiRelief.class,FiRelief::getFicaseid,FiCreditcases::getCreditcaseid)
                        .eq(FiRelief::getFicaseid,ficaseid)
                        .in(FiRelief::getStatus,
                                EnumConfirmStatus.Pending.value,
                                EnumConfirmStatus.Pass.value,
                                EnumConfirmStatus.Finish.value
                        )
        );

        return new CmdResVo(num.intValue(),ficaseid.toString());
    }

    @Override
    public int updateCallTask(Integer ficaseid,Integer taskid,Date appointdt)
    {
        if(JPubUtils.IsEmpty(ficaseid)) return 0;

        FiCreditcases fiCreditcases=new FiCreditcases();
        fiCreditcases.setCreditcaseid(ficaseid);
        fiCreditcases.setLastcctaskid(taskid);
        fiCreditcases.setLastccdt(new Date());
        if(appointdt!=null){
            fiCreditcases.setAppointdt(appointdt);
        }
        return baseMapper.updateById(fiCreditcases);
    }

    @Override
    public CmdResVo getProdType(Integer ficaseid)
    {
        FiCreditcases fi=baseMapper.selectById(ficaseid);
        if(fi==null){
            return new CmdResVo(0,"nofindficase");
        }
        return new CmdResVo(1,fi.getProdtype());
    }

    @Override
    public List<Integer> getCaseIDList(String custno)
    {
        List<Integer> list=new LinkedList<>();
        List<FiCreditcases> fiCreditcases=baseMapper.selectList(new QueryWrapper<FiCreditcases>()
                .eq("customernumber",custno)
        );
        if(JPubUtils.IsNotEmpty(fiCreditcases)){
            for (FiCreditcases fi : fiCreditcases){
                list.add(fi.getCreditcaseid());
            }
        }

        return list;
    }
    private boolean CheckDistBindday(String batchid,Integer binddays)
    {
        if(JPubUtils.IsEmpty(batchid)) return true;

        List<FiDistbatchHist> histList=fiDistbatchHistService.list(new MPJLambdaWrapper<FiDistbatchHist>()
                .eq(FiDistbatchHist::getBatchid,batchid)
                .orderByAsc(FiDistbatchHist::getActiondt)
        );
        if(JPubUtils.IsEmpty(histList))
            return true;

        boolean bret=true;
        //加入到以前的老批次
        FiDistbatchHist hist=histList.get(0);

        Date bindenddt1=JPubUtils.addDays(hist.getActiondt(),hist.getBinddays());
        Date bindenddt2=JPubUtils.addDays(new Date(),binddays);
        if(JPubUtils.IsEqual(bindenddt1,bindenddt2))
            bret=true;
        histList.clear();

        return bret;
    }

    private BigDecimal getPayAmount(Integer ficaseid)
    {//取实际还款额
        try {
            FiCreditcases fi = baseMapper.selectById(ficaseid);
            String acctno = fi.getCreditAcct();
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
            //开始时间从分案次日凌晨开始，在函数里面计算
            String distdt = sdf.format(fi.getDistdt());
            String recydt = sdf.format(new Date());
            return baseMapper.getPayAmt(acctno, distdt, recydt);
        }
        catch (Exception e){
            log.warn("getPayAmount({}).Fail:{}",ficaseid,e.getMessage());
            return new BigDecimal(0.0);
        }
    }

    @Override
    public CmdResVo getRecycleProcess()
    {
        Integer count=jCommonData.GetRecycleProcessCount();
        if(count==null||count==-1){
            return new CmdResVo(-1,"end");
        }
        return new CmdResVo(count,"running");
    }


    //案件转派或派发，2024-10-21
    @Override
    public CmdResVo fiCaseTranDist(JSysUser user, TranDistReqVo paras, List<Integer> distCaseList)
    {
        String $usrid=user.getUsrid();

        if(JPubUtils.IsEmpty(distCaseList)||distCaseList.size()==0){
            throw new ServiceException(EnumRCode.NoFindPreDist.value, EnumRCode.NoFindPreDist.name);
        }

        String distdtime=JPubUtils.DTimeToString(new Date());
        SimpleDateFormat sdf=new SimpleDateFormat("yyMMddHHmmss");
        String batchid="T"+sdf.format(new Date());

        log.info("fiCaseTranDist:getDistCount={}", distCaseList.size());

        int ret=0;
        Date distdt=new Date();
        for(Integer ficaseid:distCaseList) {//如果使用in，可能为超过1000条限制
            ret = baseMapper.update(null, new UpdateWrapper<FiCreditcases>()
                    .lambda()
                    .eq(FiCreditcases::getCreditcaseid,ficaseid)
                    .eq(FiCreditcases::getCcc, EnumFiCaseQueue.O.name)
                    .ne(FiCreditcases::getStatus, EnumFiCaseStatus.TranDist.value)
                    .set(FiCreditcases::getStatusdt, new Date())
                    .set(FiCreditcases::getStatus, EnumFiCaseStatus.TranDist.value)
                    .set(FiCreditcases::getDistdt, distdt)
                    .set(FiCreditcases::getDistbatchid, batchid)
                    .set(FiCreditcases::getDistusr, $usrid)
                    .set(FiCreditcases::getSendtodept, paras.getGrpid())
            );
            log.info("fiCaseTranDist:Update(batchid={}).ret={}", batchid, ret);
        }

        ret=baseMapper.updateToHist(batchid,distdtime);

        log.info("fiCaseTranDist:updateToHist(batchid={},distdtime={}).ret={}",batchid,distdtime,ret);

        return new CmdResVo(ret,batchid);
    }

    @Override
    public int updateToHistByID(Integer ficaseid)
    {
        return baseMapper.updateToHistByID(ficaseid);
    }

    //V2.0 改成跟案件表相符的字段名称
    @Override
    public void BuildFiCaseAmt(FiCreditcases fi,LinkedHashMap p)
    {//分案，回收，出催是需要写入记录历史
        if(p==null){
            log.warn("BuildFiCaseAmt:p isnull");
        }else {
            //逾期金额或已出账单金额
            fi.setOdudPrin((BigDecimal) p.get("odud_prin"));
            //透支本金
            fi.setOverPrin((BigDecimal) p.get("over_prin"));
            //利息
            fi.setInterestFee((BigDecimal) p.get("interest_fee"));
            //其它费用
            fi.setOthExpense((BigDecimal) p.get("oth_expense"));
            //违约金
            fi.setPenchgAcc((BigDecimal) p.get("penchg_acc"));
            fi.setMthsOdue((Integer) (p.get("mths_odue")));
            if(JPubUtils.IsEmpty(p.get("mths_day"))){
                fi.setMthsDay((Integer) (p.get("mths_day")));
            }
            if(JPubUtils.IsEmpty(p.get("repay"))) {
                fi.setRepay((BigDecimal) p.get("repay"));
            }
            if(JPubUtils.IsEmpty(p.get("loadamt"))) {
                //贷款金额
                fi.setRepay((BigDecimal) p.get("loadamt"));
            }
        }
    }

    @Override
    public List<CaseNavResVo> getCaseNavigate()
    {
        JSysUser user = jCommonData.getCurrUsr();
        String usrid = user.getUsrid();
        String grps = user.getGroups();
        String mgrps = baseMapper.getUserMgrp(user.getUsrid());
        Integer ismgr=0;
        if (JPubUtils.IsEmpty(mgrps)) {
            mgrps = grps;
            ismgr=1;
        }
        String[] arrs = mgrps.split(",");
        if(JPubUtils.IsEmpty(nav_mthsmax)){
            nav_mthsmax=6;
        }
        log.info("getCaseNavigate:mgrps={},usrid={},nav_mthsmax={}", arrs, usrid,nav_mthsmax);

        return baseMapper.getCaseNavigate(arrs,usrid,nav_mthsmax,ismgr);
    }

    @Override
    public String getOrderByFiled(String orderby)
    {
        String ret="";
        if(JPubUtils.IsEmpty(orderby)) return "";
        String s=orderby.trim().toLowerCase();
        if(JPubUtils.IsEqual(s,"ficaseid")){
            ret="t.creditcaseid";
        }
        else if(JPubUtils.IsEqual(s,"custnumber")){
            ret="t.customernumber";
        }
        else if(JPubUtils.IsEqual(s,"odudprin")){
            ret="t.odud_prin";
        }
        else if(JPubUtils.IsEqual(s,"repay")){
            ret="t.repay";
        }
        else if(JPubUtils.IsEqual(s,"indt")){
            ret="t.indt";
        }
        else if(JPubUtils.IsEqual(s,"appointdt")){
            ret="t.appointdt";
        }
        else if(JPubUtils.IsEqual(s,"lastccdt")){
            ret="t.lastccdt";
        }
        else if(JPubUtils.IsEqual(s,"recycledt")){
            ret="t.recycledt";
        }
        else if(JPubUtils.IsEqual(s,"closedt")){
            ret="t.closedt";
        }
        else if(JPubUtils.IsEqual(s,"mthsodue")){
            ret="t.mths_odue";
        }
        else if(JPubUtils.IsEqual(s,"mthsday")){
            ret="t.mths_day";
        }
        else if(JPubUtils.IsEqual(s,"distdt")){
            ret="t.distdt";
        }
        log.info("getOrderByFiled({})->{}",orderby,ret);
        return ret;
    }

    //分案批次展期，只允许批次状态是未退案状态
    @Override
    public CmdResVo fiCaseDistRoll(String usrid,DistRollVo distRollVo)
    {
        Date startdt=JPubUtils.StringToDate(distRollVo.getBindenddt());
        Date enddt=JPubUtils.StringToDate(distRollVo.getTodt());
        int iday=JPubUtils.DayDifference(startdt,enddt);

        String disbatchid=distRollVo.getBatchid();

        log.info("fiCaseDistRoll:batchid={},bindenddt={}->todt={}",
                disbatchid,distRollVo.getBindenddt(),distRollVo.getTodt()
        );

        FiDistbatchHist hist=new FiDistbatchHist();
        hist.setBatchid(distRollVo.getBatchid());
        hist.setBinddays(iday);
        hist.setMddt(new Date());
        hist.setMdusr(usrid);

        baseMapper.update(null,new UpdateWrapper<FiCreditcases>()
                .lambda()
                .set(FiCreditcases::getBindenddt,enddt)
                .eq(FiCreditcases::getDistbatchid,disbatchid)
                .in(FiCreditcases::getStatus,1,2,9)
        );

        fiCreditcasesHistService.update(new UpdateWrapper<FiCreditcasesHist>()
                .lambda()
                .set(FiCreditcasesHist::getBindenddt,enddt)
                .eq(FiCreditcasesHist::getDistbatchid,disbatchid)
                .in(FiCreditcasesHist::getStatus,1,2,9)
        );

        fiDistbatchHistService.update(new UpdateWrapper<FiDistbatchHist>()
                .lambda()
                .set(FiDistbatchHist::getBinddays,iday)
                .eq(FiDistbatchHist::getBatchid,disbatchid)
        );

        return new CmdResVo(1);
    }
}




